Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations.
1<script lang="ts">
2 import {Migrator, PlcOps} from '@pds-moover/moover'
3 import {resolve} from '$app/paths'
4 import RotationKeyDisplay from '$lib/components/RotationKeyDisplay.svelte';
5 import type {RotationKeyType} from '$lib/types';
6 import {env} from '$env/dynamic/public';
7
8 let {migrator, newHandle, newPdsUrl}: { migrator: Migrator, newHandle: string, newPdsUrl: string } = $props();
9
10 let newPds = $derived(newPdsUrl.replace('https://', ''));
11
12 //UI State
13 let errorMessage: null | string = $state(null);
14 let done = $state(false);
15 let plcStatus: null | string = $state(null);
16 let showAdvancedPlcOptions = $state(false);
17 let backupSignupMessage: null | string = $state(null);
18 let backupSignupError: null | string = $state(null);
19
20 //Input State
21 let createANewRotationKey = $state(false);
22 let signupForBackups = $state(false);
23 let plcToken = $state('');
24 let rotationKeys = $state(['', '', '', '']);
25 let newlyCreatedRotationKey: RotationKeyType | null = $state(null);
26
27
28 async function signPlcOperation(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }) {
29 event.preventDefault();
30 try {
31 plcStatus = 'Signing PLC operation...';
32 backupSignupMessage = null;
33 backupSignupError = null;
34 // Build an additional rotation keys list (user-provided and/or newly created)
35 const additionalRotationKeysToAdd: string[] = [];
36 // Generate a new rotation key if requested
37 if (createANewRotationKey) {
38
39 let plcOps = new PlcOps();
40 const created = await plcOps.createANewSecp256k1();
41 newlyCreatedRotationKey = created; // { publicKey, privateKey }
42 // Reserve the first slot for the newly created key (will appear above the PDS rotation key)
43 additionalRotationKeysToAdd.push(created.publicKey);
44 }
45 // Append any manually entered rotation keys (non-empty)
46 //TODO idk about this i need to look at it again
47 for (let i = 0; i < rotationKeys.length; i++) {
48 const k = (rotationKeys[i] || '').trim();
49 if (k) {
50 additionalRotationKeysToAdd.push(k);
51 }
52 }
53
54 //TODO nervous about this state
55 await migrator.signPlcOperation(plcToken, additionalRotationKeysToAdd);
56 plcStatus = 'PLC operation signed successfully! Your account has been MOOved to the new PDS.';
57 done = true;
58
59 if (signupForBackups) {
60 try {
61 backupSignupMessage = 'Signing you up for automated backups...';
62 //TODO nervous about this state
63 await migrator.signUpForBackupsFromMigration(`did:web:${env.PUBLIC_XRPC_BASE}`);
64 backupSignupMessage = 'Signed up for automated backups successfully.';
65 } catch (e) {
66 console.error(e);
67 //@ts-expect-error: There is a e.message, or at least a check for it
68 backupSignupError = e?.message || 'Failed to sign you up for automated backups.';
69 }
70 }
71 } catch (error) {
72 //@ts-expect-error: There is a message
73 errorMessage = error.message;
74 console.error(error);
75 }
76 }
77
78</script>
79
80
81<div class="section">
82 <form onsubmit="{signPlcOperation}">
83 {#if !done}
84 <div>
85 <h2>MOOving to <span style="text-decoration: underline">{newPds}</span></h2>
86 <p>Please check your email attached to your previous account for a PLC token to enter below</p>
87 <div class="form-group">
88 <label for="plc-token">PLC Token:</label>
89 <input type="text" id="plc-token" name="plc-token" bind:value={plcToken} required>
90 </div>
91 <p style="text-align: left">
92 Please check the boxes below if you would like to add a Rotation Key to your account and to sign up
93 for PDS MOOver's free backup service.
94 With a Rotation Key and backups if your new PDS ever goes down
95 you can recover your account and it's data. This is not required but highly recommended.</p>
96 <div class="form-group">
97 <label for="rotation-key" class="moove-checkbox-label">
98 <input bind:checked={createANewRotationKey} type="checkbox" id="rotation-key"
99 name="rotation-key">
100 <span>Create and add a new Rotation Key</span>
101 </label>
102 </div>
103 <div class="form-group">
104 <label for="backups-signup" class="moove-checkbox-label">
105 <input bind:checked={signupForBackups} type="checkbox" id="backups-signup"
106 name="backups-signup">
107 <span>Signup for automated account backups</span>
108 </label>
109 </div>
110 <div class="form-group">
111 <button type="button" id="plc-advance"
112 onclick={() => showAdvancedPlcOptions = !showAdvancedPlcOptions}>Add
113 Additional Rotation Keys
114 </button>
115 </div>
116 {#if showAdvancedPlcOptions}
117 <div class="section" style="padding-bottom: 10px;">
118 <div style="text-align: left;">
119 You can pick up to 4 rotation keys to your PLC document. These will appear above your new
120 PDS
121 rotation key so you can recover your account in the event of an adversarial take over from a
122 rogue PDS
123 </div>
124 <div class="form-group" style="margin-top: 10px;">
125 <label for="rotation-key-1">Rotation key 1</label>
126 <input type="text" id="rotation-key-1" name="rotation-key-1"
127 bind:value={rotationKeys[0]}
128 disabled={createANewRotationKey}
129 placeholder={createANewRotationKey ? 'reserved for the newly created rotation key' : ''}>
130 </div>
131 <div class="form-group">
132 <label for="rotation-key-2">Rotation key 2</label>
133 <input type="text" id="rotation-key-2" name="rotation-key-2" bind:value={rotationKeys[1]}>
134 </div>
135 <div class="form-group">
136 <label for="rotation-key-3">Rotation key 3</label>
137 <input type="text" id="rotation-key-3" name="rotation-key-3" bind:value={rotationKeys[2]}>
138 </div>
139 <div class="form-group">
140 <label for="rotation-key-4">Rotation key 4</label>
141 <input type="text" id="rotation-key-4" name="rotation-key-4" bind:value={rotationKeys[3]}>
142 </div>
143 </div>
144 {/if}
145 </div>
146 {/if}
147 {#if errorMessage}
148 <div class="error-message">{errorMessage}</div>
149 {/if}
150
151 {#if done && createANewRotationKey && newlyCreatedRotationKey}
152 <div>
153 <RotationKeyDisplay handle={newHandle} rotationKey={newlyCreatedRotationKey}/>
154 </div>
155 {/if}
156
157 {#if !done && plcStatus}
158 <div class="status-message">{plcStatus}</div>
159 {/if}
160
161 {#if signupForBackups && backupSignupMessage}
162 <div>
163 <div class="status-message">{backupSignupMessage}</div>
164 {#if backupSignupError}
165 <div class="error-message">{backupSignupError}</div>
166 {/if}
167
168 </div>
169 {/if}
170
171 {#if done}
172 <div class="status-message">Congratulations! You have MOOved to <strong>{newPdsUrl}</strong>! Remember to
173 use
174 your new PDS URL under "Hosting provider" when logging in on Bluesky. If you cannot login or see "Your
175 account is deactivated" please follow the directions
176 <a href={resolve('/info#cant-login')}>here.</a></div>
177 {:else }
178 <div>
179 <button type="submit">Sign the papers</button>
180 </div>
181 {/if}
182
183
184 </form>
185</div>